#include "legoland.h"
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string.h>
#include <cmath>

/*****************************************************************
* LEGOLAND - Data structure that holds genus/species information
*   and associated k-means cluster probabilities
*
* Created by: Bryan Quach
* Date Created: 10 April 2012
* Last Modified: 3 May 2012
******************************************************************/

using namespace std;

/*************************************************************************
* legoland - constructor
*   sets up variables to default values
**************************************************************************/
legoland::legoland()
{
    multiplier = 5000; //arbitrarily assign centroid coordinate multiplier MS: 5000 for memory issues

    /*Initialize legoville*/
//    legoville = new vector<lego>[(multiplier+1)*(multiplier+1)];
    legoville = new vector<lego> * [multiplier+1];  //MS: change from 1d to 2d array of vectors of legos
    for (int i = 0; i < multiplier + 1; i++)
    {
        legoville[i] = new vector<lego> [multiplier+1];
    }
}

/*************************************************************************
* legoland - constructor
*   parameters:
*       -index_factor: multiplier value
*   sets up variables based upon user specified multiplier value
**************************************************************************/
legoland::legoland(unsigned long long index_factor)
{
    multiplier = index_factor;

    /*Initialize legoville*/
//    legoville = new vector<lego>[(multiplier+1)*(multiplier+1)];  //MS: change from 1d to 2d array of vectors of legos
    legoville = new vector<lego> * [multiplier+1];
    for (int i = 0; i < multiplier + 1; i++)
    {
        legoville[i] = new vector<lego> [multiplier+1];
    }
}

/*************************************************************************
* get_size_legoville
*   Returns to user the size (number of elements) in legoville
**************************************************************************/
unsigned long long legoland::get_size_legoville()
{
    return (multiplier+1)*(multiplier+1);
}

/*************************************************************************
* legoland - deconstructor
*   delete dynamically allocated memory
**************************************************************************/
legoland::~legoland()
{
    for (int i = 0; i < multiplier + 1; i++)
    {
        delete legoville [i];
    }
    delete legoville;
}

/*************************************************************************
* add - add elements to legoville
*   paramters:
*       -taxon: genus/species name
*       -centroids: vector holding centroid coordinates and probabilities
*   return: true if add successful, false if otherwise
**************************************************************************/
bool legoland::add(unsigned short taxon, centroid centroids[], int numCentroids)
{
    lego l;
    l.taxon = taxon;
    unsigned long long x;
    unsigned long long y;

    /**Add to legoville**/
    for(int i = 0; i < numCentroids; i++)
    {
//        centroid cluster = centroids[i];  //MS: got rid of it
        x = floor(centroids[i].x*multiplier+0.5); //convert from float to int
        y = floor(centroids[i].y*multiplier+0.5); //convert from float to int
//        y = y*multiplier+y; //calculate "row" position for legoville array  //MS: eliminated with change from 1d to 2d array
//        x = x + y;

        l.prob = centroids[i].prob;

        /***Check if taxon already exists at the given position***/
        bool duplicate = false;
        if(legoville[x][y].size() > 0)
        {
            for(int j = 0; j < legoville[x][y].size(); j++)
            {
                if(legoville[x][y].at(j).taxon == l.taxon)
                {
                    legoville[x][y].at(j).prob += l.prob; //update existing lego
                    duplicate = true;
                    break;
                }
            }
        }
        if(duplicate == false)
        {
            legoville[x][y].push_back(l); //add lego to legoville
        }
    }
    return true;
}

/*************************************************************************
* add - add elements to legoville       //MS: changing above to work with vectors
*   paramters:
*       -taxon: genus/species name
*       -centroids: vector holding centroid coordinates and probabilities
*   return: true if add successful, false if otherwise
**************************************************************************/
bool legoland::add(unsigned short taxon, vector<centroid> centroids)
{
    lego l;
    l.taxon = taxon;
    unsigned long long x;
    unsigned long long y;

    /**Add to legoville**/
    for(int i = 0; i < centroids.size(); i++)
    {
//        centroid cluster = centroids[i];
        x = floor(centroids[i].x*multiplier+0.5); //convert from float to int
        y = floor(centroids[i].y*multiplier+0.5); //convert from float to int
//        y = y*multiplier+y; //calculate "row" position for legoville array
//        x = x + y;

        l.prob = centroids[i].prob;

        /***Check if taxon already exists at the given position***/
        bool duplicate = false;
        if(legoville[x][y].size() > 0)
        {
            for(int j = 0; j < legoville[x][y].size(); j++)
            {
                if(legoville[x][y].at(j).taxon == l.taxon)
                {
                    legoville[x][y].at(j).prob += l.prob; //update existing lego
                    duplicate = true;
                    break;
                }
            }
        }
        if(duplicate == false)
        {
            legoville[x][y].push_back(l); //add lego to legoville
        }
    }
    return true;
}

/*************************************************************************
* add - add elements to legoville       //MS: add w/o taxon code which becomes randomly assigned, good for library of 1 genome on legoland
*   paramters:
*       -taxon: genus/species name
*       -centroids: vector holding centroid coordinates and probabilities
*   return: true if add successful, false if otherwise
**************************************************************************/
bool legoland::add(vector<centroid> centroids)
{
    lego l;
    unsigned long long x;
    unsigned long long y;

    /**Add to legoville**/
    for(int i = 0; i < centroids.size(); i++)
    {
//        centroid cluster = centroids[i];
        x = floor(centroids[i].x*multiplier+0.5); //convert from float to int
        y = floor(centroids[i].y*multiplier+0.5); //convert from float to int
//        y = y*multiplier+y; //calculate "row" position for legoville array
//        x = x + y;

        l.prob = centroids[i].prob;

        /***Check if taxon already exists at the given position***/
        bool duplicate = false;
        if(legoville[x][y].size() > 0)
        {
            legoville[x][y][0].prob += l.prob; //update existing lego
        }
        else
        {
            legoville[x][y].push_back(l); //add lego to legoville
        }
    }
    return true;
}

///*****************************************************************************************
//* load_legos - adds a vector of legos to a specific index in legoville
//*   parameters:
//*       -index_factor: index in legoville
//*       -v: vector of legos (genus/species code and probabilities) at the coordinate given
//*   returns true if loaded else false (if out of range)
//******************************************************************************************/
//bool legoland::load_legos(unsigned long long index_factor, vector<lego> v)
//{
//    if(index_factor>((multiplier+1)*(multiplier+1)))
//        return false;
//    legoville[index_factor]=v;
//    return true;
//}

/*****************************************************************************************
* load_legos - adds a vector of legos to a specific index in legoville
*   parameters:
*       -index_factor: index in legoville
*       -v: vector of legos (genus/species code and probabilities) at the coordinate given
*   returns true if loaded else false (if out of range)
******************************************************************************************/
bool legoland::load_legos(int x, int y, vector<lego> v)
{
    if(x>multiplier+1 && y>multiplier+1)
        return false;
    legoville[x][y]=v;
    return true;
}

/*****************************************************************************************
* search - find lego within legoville using centroid coordinates
*   parameters:
*       -c: coordinates for a particular DNA read
*   return: vector of legos (genus/species code and probabilities) at the coordinate given
******************************************************************************************/
vector<lego> legoland::search(coordinates c)
{
    unsigned long long int x = floor(c.x*multiplier+0.5);
    unsigned long long int y = floor(c.y*multiplier+0.5);
    return legoville[x][y];
//    y = y * multiplier + y;  //calculate "row" position for legoville array  //MS: removed transformation in change from 1d to 2d
//    x = x + y;

}//end search

/*****************************************************************************************
* search - find lego within legoville using centroid coordinates
*   parameters:
*       -c: coordinates for a particular DNA read
*   return: vector of legos (genus/species code and probabilities) at the coordinate given
******************************************************************************************/
vector<lego> legoland::search(coordinates c, int sensi)
{
    unsigned long long int x = floor(c.x*multiplier+0.5);
    unsigned long long int y = floor(c.y*multiplier+0.5);
    vector<lego> result = legoville[x][y];
    for (int i = x-sensi; i < x+sensi; i++) {
        for (int j= y-sensi; j < y+sensi; j++) {
            for (int k = 0; k < legoville[i][j].size(); k ++)
            {
                result.push_back(legoville[i][j][k]);
            }
        }
    }
    return result;
//    y = y * multiplier + y;  //calculate "row" position for legoville array  //MS: elimination in change from 1d to 2d
//    x = x + y;

}//end search

///********************************************************************************************
//* search - find lego within legoville using an index position between 0 and size of legoville     //elimination in change from 1d to 2d
//*   parameters:
//*       -lego_index: index position of lego vector in legoville
//*   return: vector of legos (genus/species code and probabilities)
//*********************************************************************************************/
//vector<lego> legoland::search(unsigned long long lego_index)
//{
//    return legoville[lego_index];
//}//end search

/********************************************************************************************
* search - find lego within legoville using an index position between 0 and size of legoville
*   parameters:
*       -lego_index: index position of lego vector in legoville
*   return: vector of legos (genus/species code and probabilities)
*********************************************************************************************/
vector<lego> legoland::search(int x, int y)
{
    return legoville[x][y];
}//end search


/*****************************************************************************************
* get_multiplier - accessor method to get the multiplier
*   parameters:
*       none
*   return: multiplier of type unsigned long long
******************************************************************************************/
unsigned long long legoland::get_multiplier()
{
    return multiplier;
}

/*****************************************************************************************
* change_multiplier - update the multiplier
*   parameters:
*       none
*   return: n/a
******************************************************************************************/
void legoland::change_multiplier(unsigned long long m)
{
    multiplier = m;
}

/*****************************************************************************************
* printLegoland - takes legoland and prints to a file w/ values at x, y coords according
*               to the position on legoland and values based on number of taxa at that
*               spot
*   parameters:
*       none
*   return: n/a
******************************************************************************************/
void legoland::printLegoland()
{
    ofstream out;
    out.open("legoland.txt");
    if (!out.is_open())
    {
        cout << "could not open file to print legoland" << endl;
    }
    for (int i = 0; i < multiplier+1; i++)
    {
        out << legoville[i][0].size();
        for (int j = 1; j < multiplier+1; j++)
        {
            out << '\t' << legoville[i][j].size();
        }
        out << endl;
    }
    out.clear();
    out.close();
}
